package com.thread.threadfirst.studyThread1;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 17、接着对上面的16优化：
 * 这次试用wait()、notify()，wait()释放锁、notify()不会释放锁
 * 注意：运用这种方法，必须要保证t2先执行，即首先让t2监听才可以
 * notify()之后，t1必须释放锁，t2退出后，也必须notify，通知t1继续执行。
 * <p>
 * 问：为什么不能一开始就执行从1加到10的t1线程，在加到5时，调用wait()，然后另一个当加到5时就结束的t2线程中执行notify()这样呢？？？
 * 回答上面的这个问题：线程的执行顺序是需要JVM来线程调度的，如果先执行了t2线程后执行t1线程呢，这就需要在t2线程中执行wait()，在t1线程中执行notify()了，
 * 而如果先执行了t1线程后执行t2线程，则t1线程中有wait()，t2线程中有notify()，下面在t2线程执行后加了sleep()1秒，就让t2线程优先于t1线程执行了。
 * 加了synchronized住o对象就解决了上个16留下的不精确严谨的问题，所以重点是可以获得o对象的锁来控制多个线程的数据不安全问题。
 */
public class T9 {

    //添加volatile使t2能够得到通知
    volatile List list = new ArrayList();

    public void add(Object o) {
        list.add(o);
    }

    public int size() {
        return list.size();
    }


    public static void main(String[] args) {
        T9 t9 = new T9();

        final Object o = new Object();

        new Thread(() -> {
            synchronized (o) {
                System.out.println("t2开始……");
                if (t9.size() != 5) {
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2结束……");
                //通知t1继续执行
                o.notify();
            }
        }, "t2").start();

        //慢一秒再让t1线程执行
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            System.out.println("t1开始……");
            synchronized (o) {
                for (int i = 0; i < 10; i++) {
                    t9.add(new Object());
                    System.out.println("add " + i);

                    if (t9.size() == 5) {
                        o.notify();
                        //该线程释放锁，让t2继续执行
                        try {
                            o.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }, "t1").start();
    }
}
